package cn.flying.cloud.gateway.oauth.configuration.authentication;

import javax.annotation.Resource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.web.server.SecurityWebFilterChain;

import reactor.core.publisher.Mono;

import cn.flying.cloud.gateway.oauth.configuration.authorization.AuthorizationManager;
import cn.flying.cloud.gateway.oauth.handler.RequestAccessDeniedHandler;
import cn.flying.cloud.gateway.oauth.handler.RequestAuthenticationEntryPoint;

/**
 * 资源服务器配置
 *
 * @author: admin
 * @date: 2023年06月26日 10:15
 * @version: 1.0
 */
@Configuration
@EnableWebFluxSecurity
public class WebFluxSecurityConfig {

    @Resource
    private AuthorizationManager authorizationManager;
    @Resource
    private RequestAccessDeniedHandler requestAccessDeniedHandler;
    @Resource
    private RequestAuthenticationEntryPoint requestAuthenticationEntryPoint;
    @Resource
    private WhiteListConfig whiteListConfig;

    @Bean
    public SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) {
        http.oauth2ResourceServer()
                .jwt()
                .jwtAuthenticationConverter(jwtAuthenticationConverter())
                //自定义处理JWT请求头过期或签名错误的结果
                .and()
                // 还没有认证时发生认证异常，比如token过期，token不合法
                .authenticationEntryPoint(requestAuthenticationEntryPoint)
                // 认证成功后没有权限操作
                .accessDeniedHandler(requestAccessDeniedHandler)
                .and()
                .authorizeExchange()
                //白名单配置
                .pathMatchers(whiteListConfig.getUris().toArray(new String[0])).permitAll()
                //鉴权管理器配置，所有的请求都交由此处进行权限判断处理
                .anyExchange().access(authorizationManager)
                .and()
                .exceptionHandling()
                //处理未授权
                .accessDeniedHandler(requestAccessDeniedHandler)
                //处理未认证
                .authenticationEntryPoint(requestAuthenticationEntryPoint)
                .and().csrf().disable();
        //对白名单路径，直接移除JWT请求头
//        http.addFilterBefore(whiteUrisRemoveJwtFilter, SecurityWebFiltersOrder.AUTHENTICATION);

//        http.oauth2Login();

        return http.build();
    }

    /**
     * 自定义 JwtAuthenticationConverter，解决convert() 方法提供的角色默认是以 SCOPE_ 开头，但 hasRole() 是以 ROLE_ 开头
     *
     * @return
     */
    @Bean
    public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
        //转换权限信息，从 JWT 中转换出来的权限不需要前缀（默认前缀 SCOPE_，即 资源服务器 声明权限时需要带上此前缀
        JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
        jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");

        //转换认证信息
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
    }

}